{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Python函数基础\n",
    "\n",
    "在本课中，我们将学习Python函数的基本概念、定义和使用方法。函数是组织代码的重要工具，能够提高代码的可读性、重用性和可维护性。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. 函数基础\n",
    "\n",
    "### 1.1 什么是函数？\n",
    "\n",
    "函数是执行特定任务的代码块，它可以接收输入（参数），执行特定的操作，并返回结果。函数有助于我们将大型程序分解成可管理的、独立的部分。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.2 为什么使用函数？\n",
    "\n",
    "使用函数有以下几个主要优点：\n",
    "\n",
    "1. **代码重用**：一次编写，多次使用\n",
    "2. **代码组织**：将相关的代码组织到一起\n",
    "3. **可维护性**：更容易修改和维护\n",
    "4. **抽象**：隐藏实现细节，只关注功能\n",
    "5. **模块化**：将程序分解为管理更加容易的小块"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.3 定义函数\n",
    "\n",
    "在Python中，使用`def`关键字定义函数，基本语法如下：\n",
    "\n",
    "```python\n",
    "def 函数名(参数1, 参数2, ...):\n",
    "    \"\"\"文档字符串（可选）\"\"\"\n",
    "    # 函数体\n",
    "    # 执行操作\n",
    "    return 返回值  # 可选\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hello, World!\n"
     ]
    }
   ],
   "source": [
    "# 定义一个简单的函数\n",
    "def say_hello():\n",
    "    \"\"\"这个函数打印'Hello, World!'\"\"\"\n",
    "    print(\"Hello, World!\")\n",
    "    \n",
    "# 调用函数\n",
    "say_hello()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hello, Alice!\n",
      "Hello, Bob!\n"
     ]
    }
   ],
   "source": [
    "# 定义一个带参数的函数\n",
    "def greet(name):\n",
    "    \"\"\"这个函数向指定的人打招呼\"\"\"\n",
    "    print(f\"Hello, {name}!\")\n",
    "    \n",
    "# 调用带参数的函数\n",
    "greet(\"Alice\")\n",
    "greet(\"Bob\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "5 + 3 = 8\n",
      "10 + 20 = 30\n"
     ]
    }
   ],
   "source": [
    "# 定义一个带返回值的函数\n",
    "def add_numbers(a, b):\n",
    "    \"\"\"这个函数返回两个数的和\"\"\"\n",
    "    return a + b\n",
    "\n",
    "# 调用带返回值的函数\n",
    "result = add_numbers(5, 3)\n",
    "print(f\"5 + 3 = {result}\")\n",
    "\n",
    "# 可以直接在表达式中使用\n",
    "print(f\"10 + 20 = {add_numbers(10, 20)}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.4 函数的文档字符串（Docstring）\n",
    "\n",
    "文档字符串是一种在函数开头使用三重引号（`\"\"\"` 或 `'''`）包围的字符串，用于说明函数的功能、参数和返回值。良好的文档字符串有助于他人（以及未来的你）理解函数的用途和用法。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "计算矩形的面积。\n",
      "\n",
      "    参数:\n",
      "        length (float): 矩形的长度\n",
      "        width (float): 矩形的宽度\n",
      "\n",
      "    返回:\n",
      "        float: 矩形的面积\n",
      "    \n",
      "Help on function calculate_area in module __main__:\n",
      "\n",
      "calculate_area(length, width)\n",
      "    计算矩形的面积。\n",
      "    \n",
      "    参数:\n",
      "        length (float): 矩形的长度\n",
      "        width (float): 矩形的宽度\n",
      "    \n",
      "    返回:\n",
      "        float: 矩形的面积\n",
      "\n",
      "Prints the values to a stream, or to sys.stdout by default.\n",
      "\n",
      "  sep\n",
      "    string inserted between values, default a space.\n",
      "  end\n",
      "    string appended after the last value, default a newline.\n",
      "  file\n",
      "    a file-like object (stream); defaults to the current sys.stdout.\n",
      "  flush\n",
      "    whether to forcibly flush the stream.\n"
     ]
    }
   ],
   "source": [
    "def calculate_area(length, width):\n",
    "    \"\"\"计算矩形的面积。\n",
    "    \n",
    "    参数:\n",
    "        length (float): 矩形的长度\n",
    "        width (float): 矩形的宽度\n",
    "        \n",
    "    返回:\n",
    "        float: 矩形的面积\n",
    "    \"\"\"\n",
    "    return length * width\n",
    "\n",
    "# 查看函数的文档字符串\n",
    "print(calculate_area.__doc__)\n",
    "\n",
    "# 使用help()函数查看函数的帮助信息\n",
    "help(calculate_area)\n",
    "\n",
    "print(print.__doc__)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. 函数参数\n",
    "\n",
    "Python函数支持多种参数类型，使函数的定义和调用更加灵活。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.1 位置参数（Positional Arguments）\n",
    "\n",
    "位置参数是按照定义时的顺序传递给函数的参数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "我有一只咪咪，它叫猫。\n",
      "我有一只狗，它叫旺财。\n"
     ]
    }
   ],
   "source": [
    "def describe_pet(animal_type, pet_name):\n",
    "    \"\"\"显示宠物信息\"\"\"\n",
    "    print(f\"我有一只{animal_type}，它叫{pet_name}。\")\n",
    "    \n",
    "# 使用位置参数调用函数\n",
    "describe_pet(\"猫\",\"咪咪\")\n",
    "describe_pet(\"狗\", \"旺财\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.2 关键字参数（Keyword Arguments）\n",
    "\n",
    "关键字参数是在调用函数时，明确指定参数名称的参数。使用关键字参数可以不按照定义时的顺序传递参数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "我有一只兔子，它叫小白。\n",
      "我有一只鱼，它叫金金。\n",
      "我有一只仓鼠，它叫花花。\n"
     ]
    }
   ],
   "source": [
    "# 使用关键字参数调用函数\n",
    "describe_pet(pet_name=\"小白\", animal_type=\"兔子\")\n",
    "describe_pet(animal_type=\"鱼\", pet_name=\"金金\")\n",
    "\n",
    "# 混合使用位置参数和关键字参数\n",
    "# 注意：位置参数必须在关键字参数之前\n",
    "describe_pet(\"仓鼠\", pet_name=\"花花\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.3 默认参数值（Default Parameter Values）\n",
    "\n",
    "可以为函数参数指定默认值，当调用函数时如果没有提供这个参数，就会使用默认值。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def describe_pet(pet_name, animal_type=\"狗\"):\n",
    "    \"\"\"显示宠物信息，默认动物类型为狗\"\"\"\n",
    "    print(f\"我有一只{animal_type}，它叫{pet_name}。\")\n",
    "    \n",
    "# 使用默认参数值\n",
    "describe_pet(\"旺财\")  # 不指定animal_type，使用默认值\"狗\"\n",
    "describe_pet(\"咪咪\", \"猫\")  # 覆盖默认值\n",
    "describe_pet(pet_name=\"小花\", animal_type=\"兔子\")  # 使用关键字参数覆盖默认值"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.4 可变长度参数列表\n",
    "\n",
    "Python函数允许接收不确定数量的参数，有两种方式："
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2.4.1 *args（Variable-Length Positional Arguments）\n",
    "\n",
    "使用`*args`接收任意数量的位置参数，这些参数会被打包成一个元组。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "参数类型: <class 'tuple'>\n",
      "参数: (1, 2)\n",
      "总和: 3\n",
      "参数类型: <class 'tuple'>\n",
      "参数: (1, 2, 3, 4, 5)\n",
      "总和: 15\n",
      "参数类型: <class 'tuple'>\n",
      "参数: ()\n",
      "总和: 0\n"
     ]
    }
   ],
   "source": [
    "def sum_all(*numbers):\n",
    "    \"\"\"计算所有数字的和\"\"\"\n",
    "    print(f\"参数类型: {type(numbers)}\")\n",
    "    print(f\"参数: {numbers}\")\n",
    "    return sum(numbers)\n",
    "\n",
    "# 调用带有不同数量参数的函数\n",
    "print(f\"总和: {sum_all(1, 2)}\")\n",
    "print(f\"总和: {sum_all(1, 2, 3, 4, 5)}\")\n",
    "print(f\"总和: {sum_all()}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2.4.2 **kwargs（Variable-Length Keyword Arguments）\n",
    "\n",
    "使用`**kwargs`接收任意数量的关键字参数，这些参数会被打包成一个字典。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "用户信息:\n",
      "first_name: Albert\n",
      "last_name: Einstein\n",
      "location: Princeton\n",
      "field: Physics\n",
      "birth_year: 1879\n"
     ]
    }
   ],
   "source": [
    "def build_profile(first_name, last_name, **user_info):\n",
    "    \"\"\"创建一个包含用户信息的字典\"\"\"\n",
    "    profile = {}\n",
    "    profile[\"first_name\"] = first_name\n",
    "    profile[\"last_name\"] = last_name\n",
    "    \n",
    "    # 添加其它所有信息\n",
    "    for key, value in user_info.items():\n",
    "        profile[key] = value\n",
    "        \n",
    "    return profile\n",
    "\n",
    "# 调用函数并传递额外的关键字参数\n",
    "user_profile = build_profile(\"Albert\", \"Einstein\",\n",
    "                             location=\"Princeton\",\n",
    "                             field=\"Physics\",\n",
    "                             birth_year=1879)\n",
    "\n",
    "print(\"用户信息:\")\n",
    "for key, value in user_profile.items():\n",
    "    print(f\"{key}: {value}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.5 参数解包（Unpacking Arguments）\n",
    "\n",
    "可以使用`*`和`**`运算符解包列表、元组或字典，将它们作为位置参数或关键字参数传递给函数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "张三今年25岁，来自北京。\n",
      "李四今年30岁，来自上海。\n"
     ]
    }
   ],
   "source": [
    "# 定义一个需要三个参数的函数\n",
    "def display_person(name, age, city):\n",
    "    print(f\"{name}今年{age}岁，来自{city}。\")\n",
    "\n",
    "\n",
    "# 使用列表解包\n",
    "person_info = [\"张三\", 25, \"北京\"]\n",
    "display_person(*person_info)  # 等同于 display_person(\"张三\", 25, \"北京\")\n",
    "\n",
    "# 使用字典解包\n",
    "person_dict = {\"name\": \"李四\", \"age\": 30, \"city\": \"上海\"}\n",
    "display_person(**person_dict)  # 等同于 display_person(name=\"李四\", age=30, city=\"上海\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.6 强制关键字参数（Keyword-Only Arguments）\n",
    "\n",
    "在Python中，可以使用`*`来指定之后的参数必须作为关键字参数传递。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hello, Alice!\n",
      "Hi, Bob!\n",
      "Hey, Charlie...\n",
      "错误: greet() takes 1 positional argument but 2 were given\n"
     ]
    }
   ],
   "source": [
    "def greet(name, *, greeting=\"Hello\", punctuation=\"!\"):\n",
    "    \"\"\"向某人打招呼，greeting和punctuation必须作为关键字参数传递\"\"\"\n",
    "    return f\"{greeting}, {name}{punctuation}\"\n",
    "\n",
    "# 正确的调用方式\n",
    "print(greet(\"Alice\"))  # 使用默认值\n",
    "print(greet(\"Bob\", greeting=\"Hi\"))  # 指定greeting\n",
    "print(greet(\"Charlie\", greeting=\"Hey\", punctuation=\"...\"))  # 指定所有参数\n",
    "\n",
    "# 错误的调用方式\n",
    "try:\n",
    "    print(greet(\"David\", \"Hi\"))  # 这会引发错误，因为第二个参数必须作为关键字参数传递\n",
    "except TypeError as e:\n",
    "    print(f\"错误: {e}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3. 返回值\n",
    "\n",
    "函数可以通过`return`语句返回值。如果没有`return`语句，函数默认返回`None`。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "5的平方是25\n",
      "周长: 30, 面积: 50\n",
      "返回值类型: <class 'tuple'>\n",
      "周长: 14, 面积: 12\n",
      "Hello!\n",
      "函数返回值: None\n"
     ]
    }
   ],
   "source": [
    "# 返回单个值\n",
    "def square(number):\n",
    "    \"\"\"返回数字的平方\"\"\"\n",
    "    return number ** 2\n",
    "\n",
    "result = square(5)\n",
    "print(f\"5的平方是{result}\")\n",
    "\n",
    "# 返回多个值（实际上是返回一个元组）\n",
    "def get_dimensions(width, height):\n",
    "    \"\"\"返回矩形的周长和面积\"\"\"\n",
    "    perimeter = 2 * (width + height)\n",
    "    area = width * height\n",
    "    return perimeter, area\n",
    "\n",
    "perimeter, area = get_dimensions(5, 10)\n",
    "print(f\"周长: {perimeter}, 面积: {area}\")\n",
    "\n",
    "# 也可以直接接收返回的元组\n",
    "dimensions = get_dimensions(3, 4)\n",
    "print(f\"返回值类型: {type(dimensions)}\")\n",
    "print(f\"周长: {dimensions[0]}, 面积: {dimensions[1]}\")\n",
    "\n",
    "# 不返回任何值的函数\n",
    "def greet_user():\n",
    "    \"\"\"简单的问候函数，不返回任何值\"\"\"\n",
    "    print(\"Hello!\")\n",
    "    \n",
    "result = greet_user()\n",
    "print(f\"函数返回值: {result}\")  # 输出: None"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.1 提前返回\n",
    "\n",
    "`return`语句会立即退出函数，可以使用这一特性在满足某个条件时提前返回结果，避免不必要的计算。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "5.0\n",
      "错误：除数不能为0\n",
      "None\n"
     ]
    }
   ],
   "source": [
    "def divide(a, b):\n",
    "    \"\"\"除法函数，处理除数为0的情况\"\"\"\n",
    "    if b == 0:\n",
    "        print(\"错误：除数不能为0\")\n",
    "        return None  # 提前返回\n",
    "    \n",
    "    # 只有在b不为0时才会执行到这里\n",
    "    return a / b\n",
    "\n",
    "print(divide(10, 2))\n",
    "print(divide(10, 0))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4. 函数作用域和变量\n",
    "\n",
    "每个函数都有自己的作用域，决定了其中定义的变量的可见性和生命周期。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4.1 局部变量和全局变量\n",
    "\n",
    "- **局部变量**：在函数内部定义的变量，只在函数内部可见，函数执行完毕后会被销毁\n",
    "- **全局变量**：在函数外部定义的变量，在整个程序中都可见"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "全局变量: Hello, World!\n",
      "Hello, World!\n",
      "Hello, Local!\n",
      "Modified Message\n",
      "函数调用后的全局变量: Hello, World!\n",
      "全局变量\n"
     ]
    }
   ],
   "source": [
    "# 全局变量\n",
    "message = \"Hello, World!\"\n",
    "\n",
    "def print_message():\n",
    "    # 访问全局变量\n",
    "    print(message)\n",
    "    \n",
    "def create_local_message():\n",
    "    # 局部变量\n",
    "    message = \"Hello, Local!\"\n",
    "    print(message)\n",
    "    \n",
    "def try_modify_global():\n",
    "    # 尝试修改全局变量（不会影响全局作用域）\n",
    "    message = \"Modified Message\"  # 这实际上创建了一个新的局部变量\n",
    "    print(message)\n",
    "\n",
    "print(\"全局变量:\", message)\n",
    "print_message()\n",
    "create_local_message()\n",
    "try_modify_global()\n",
    "print(\"函数调用后的全局变量:\", message)  # 全局变量没有被修改\n",
    "\n",
    "message = \"全局变量\"\n",
    "\n",
    "print(message)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4.2 修改全局变量\n",
    "\n",
    "使用`global`关键字可以在函数内部修改全局变量。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "初始计数器: 0\n",
      "函数内计数器: 2\n",
      "函数内计数器: 2\n",
      "函数调用后的计数器: 0\n"
     ]
    }
   ],
   "source": [
    "counter = 0\n",
    "\n",
    "def increment():\n",
    "    \"\"\"递增全局计数器\"\"\"\n",
    "    global counter  # 声明使用全局变量\n",
    "    counter += 1\n",
    "    print(f\"函数内计数器: {counter}\")\n",
    "    \n",
    "print(f\"初始计数器: {counter}\")\n",
    "increment()\n",
    "increment()\n",
    "print(f\"函数调用后的计数器: {counter}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 6. 实际应用示例"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6.1 计算器函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def calculator(a, b, operation=\"add\"):\n",
    "    \"\"\"简单计算器函数\n",
    "    \n",
    "    参数:\n",
    "        a (float): 第一个数\n",
    "        b (float): 第二个数\n",
    "        operation (str): 操作类型，可选值包括\"add\"、\"subtract\"、\"multiply\"和\"divide\"\n",
    "        \n",
    "    返回:\n",
    "        float: 计算结果，如果操作无效或除数为0则返回None\n",
    "    \"\"\"\n",
    "    if operation == \"add\":\n",
    "        return a + b\n",
    "    elif operation == \"subtract\":\n",
    "        return a - b\n",
    "    elif operation == \"multiply\":\n",
    "        return a * b\n",
    "    elif operation == \"divide\":\n",
    "        if b == 0:\n",
    "            print(\"错误: 除数不能为0\")\n",
    "            return None\n",
    "        return a / b\n",
    "    else:\n",
    "        print(f\"错误: 无效的操作'{operation}'\")\n",
    "        return None\n",
    "\n",
    "# 测试计算器函数\n",
    "print(f\"10 + 5 = {calculator(10, 5)}\")\n",
    "print(f\"10 - 5 = {calculator(10, 5, 'subtract')}\")\n",
    "print(f\"10 * 5 = {calculator(10, 5, 'multiply')}\")\n",
    "print(f\"10 / 5 = {calculator(10, 5, 'divide')}\")\n",
    "print(f\"10 / 0 = {calculator(10, 0, 'divide')}\")\n",
    "print(f\"无效操作: {calculator(10, 5, 'power')}\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
